package com.truckdispatching.image;

import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;

public class ImageLoader {

	private static final Object IMAGE_LOADER_LOCK = new Object();

	private static ImageLoader instance;

	private final int POOL_SIZE = 2;
	private final int MAX_POOL_SIZE = 2;

	private static String APP_ROOT_PATH;
	private static String EXTERNAL_IMAGE_DIR;
	private static String EXTERNAL_IMAGE_CACHE_DIR;

	private HashMap<String, ImageFileLoadTask> mImageFileLoadTaskQueue = null;
	private ThreadPoolExecutor mImageFileLoadTaskThreadPool = null;

	private HashMap<String, ImageDownloadTask> mImageDownloadTaskQueue = null;
	private ThreadPoolExecutor mImageDownloadTaskThreadPool = null;

	private ImageCache mImageCache;

	private ImageLoader() {
		mImageFileLoadTaskQueue = new HashMap<String, ImageFileLoadTask>();
		mImageFileLoadTaskThreadPool = new ThreadPoolExecutor(POOL_SIZE, MAX_POOL_SIZE, 0, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(100, new PriorityComparator()), new ThreadPoolExecutor.DiscardOldestPolicy());

		mImageDownloadTaskQueue = new HashMap<String, ImageDownloadTask>();
		mImageDownloadTaskThreadPool = new ThreadPoolExecutor(POOL_SIZE, MAX_POOL_SIZE, 0, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(100, new PriorityComparator()), new ThreadPoolExecutor.DiscardOldestPolicy());

		mImageCache = new ImageCache();
	}

	public synchronized static ImageLoader getInstance() {
		if (instance == null) {
			instance = new ImageLoader();
		}
		return instance;
	}

	public void initWithContext(Context context) {
		APP_ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/com.truckdispatching";
		EXTERNAL_IMAGE_DIR = APP_ROOT_PATH + "/image";
		EXTERNAL_IMAGE_CACHE_DIR = APP_ROOT_PATH + "/cache/image";
		File imageDir = new File(EXTERNAL_IMAGE_DIR);
		if (!imageDir.exists()) {
			imageDir.mkdirs();
		}
		File imageCacheDir = new File(EXTERNAL_IMAGE_CACHE_DIR);
		if (!imageCacheDir.exists()) {
			imageCacheDir.mkdirs();
		}
	}

	public Bitmap loadBitmap(ImageTask task, final ImageTaskListener listener) {
		synchronized (IMAGE_LOADER_LOCK) {
			Bitmap bitmap = loadBitmapFromCache(task);
			if (bitmap != null) {
				return bitmap;
			} else {
				loadBitmapFromFile(task, new ImageTaskListener() {

					@Override
					public void success(ImageTask task, Bitmap bitmap) {
						loadBitmapSuccess(task, bitmap, listener);
					}

					@Override
					public void fail(ImageTask task) {
						loadBitmapFromNet(task, new ImageTaskListener() {

							@Override
							public void success(ImageTask task, Bitmap bitmap) {
								loadBitmapSuccess(task, bitmap, listener);
							}

							@Override
							public void fail(ImageTask task) {

							}
						});
					}
				});
				return null;
			}
		}
	}

	public synchronized Bitmap loadBitmapFromCache(ImageTask task) {
		if (mImageCache != null) {
			return mImageCache.get(task);
		} else {
			return null;
		}
	}

	public void loadBitmapFromFile(ImageTask task, final ImageTaskListener listener) {
		synchronized (IMAGE_LOADER_LOCK) {
			ImageFileLoadTask loadTask = mImageFileLoadTaskQueue.get(task.mImageUrl);
			ImageTaskListener loadTaskListener = new ImageTaskListener() {

				@Override
				public void success(ImageTask task, Bitmap bitmap) {
					mImageFileLoadTaskQueue.remove(task.mImageUrl);
					loadBitmapSuccess(task, bitmap, listener);
				}

				@Override
				public void fail(ImageTask task) {
					mImageFileLoadTaskQueue.remove(task.mImageUrl);
					loadBitmapFail(task, listener);
				}
			};
			boolean isReturnBitmap = listener != null ? true : false;
			if (loadTask != null) {
				loadTask.setImageTaskListener(loadTaskListener, isReturnBitmap);
			} else {
				loadTask = new ImageFileLoadTask(task);
				loadTask.setImageTaskListener(loadTaskListener, isReturnBitmap);
				mImageFileLoadTaskQueue.put(task.mImageUrl, loadTask);
				mImageFileLoadTaskThreadPool.submit(loadTask);
			}
		}
	}

	public void loadBitmapFromNet(ImageTask task, final ImageTaskListener listener) {
		synchronized (IMAGE_LOADER_LOCK) {
			ImageDownloadTask downloadTask = mImageDownloadTaskQueue.get(task.mImageUrl);
			ImageTaskListener downloadTaskListener = new ImageTaskListener() {

				@Override
				public void success(ImageTask task, Bitmap bitmap) {
					mImageDownloadTaskQueue.remove(task.mImageUrl);
					loadBitmapSuccess(task, bitmap, listener);
				}

				@Override
				public void fail(ImageTask task) {
					mImageDownloadTaskQueue.remove(task.mImageUrl);
					loadBitmapFail(task, listener);
				}
			};
			boolean isReturnBitmap = listener != null ? true : false;
			if (downloadTask != null) {
				downloadTask.setImageTaskListener(downloadTaskListener, isReturnBitmap);
			} else {
				downloadTask = new ImageDownloadTask(task);
				downloadTask.setImageTaskListener(downloadTaskListener, isReturnBitmap);
				mImageDownloadTaskQueue.put(task.mImageUrl, downloadTask);
				mImageDownloadTaskThreadPool.submit(downloadTask);
			}
		}
	}

	private synchronized void loadBitmapSuccess(ImageTask task, Bitmap bitmap, ImageTaskListener listener) {
		if (listener != null) {
			Bitmap tempBitmap = mImageCache.put(task, bitmap);
			if (tempBitmap != null) {
				if (listener instanceof ImageTaskHandler) {
					((ImageTaskHandler) listener).obtainMessage(ImageTaskHandler.LOAD_SUCESS, new Object[] { task, bitmap }).sendToTarget();
				} else {
					listener.success(task, bitmap);
				}
			}
		} else {
			if (bitmap != null && !bitmap.isRecycled()) {
				bitmap.recycle();
			}
		}
	}

	private synchronized void loadBitmapFail(ImageTask task, ImageTaskListener listener) {
		if (listener != null) {
			if (listener instanceof ImageTaskHandler) {
				((ImageTaskHandler) listener).obtainMessage(ImageTaskHandler.LOAD_FAIL, task).sendToTarget();
			} else {
				listener.fail(task);
			}
		}
	}

	private class PriorityComparator implements Comparator<Runnable> {
		private final int HEIGHEST_PRIORITY = -3421;

		@Override
		public int compare(Runnable lhs, Runnable rhs) {
			if (lhs == null && rhs == null) {
				return 0;
			} else if (lhs == null) {
				return -1;
			} else if (rhs == null) {
				return 1;
			}
			if (!(lhs instanceof ImageTask) || !(rhs instanceof ImageTask)) {
				return 1;
			}
			final ImageTask iL = (ImageTask) lhs;
			final ImageTask iR = (ImageTask) rhs;
			if (iL.Priority == iR.Priority) {
				return 0;
			}
			if (HEIGHEST_PRIORITY == iL.Priority) {
				return 1;
			}
			if (HEIGHEST_PRIORITY == iR.Priority) {
				return -1;
			}
			return iL.Priority > iR.Priority ? 1 : -1;
		}
	}
}
